Skip to content

feat: git history in lecture headers (PLAN Phase 1)#83

Open
mmcky wants to merge 8 commits into
mainfrom
feat/git-history-headers
Open

feat: git history in lecture headers (PLAN Phase 1)#83
mmcky wants to merge 8 commits into
mainfrom
feat/git-history-headers

Conversation

@mmcky

@mmcky mmcky commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Phase 1 of PLAN.md — reproduces the quantecon-book-theme page header: a "Last changed: ⟨date⟩" control that expands into a changelog dropdown of the last N commits, with GitHub-linked commit hashes and a "full history" link.

Closed Open
muted line under the title block popover: hash → commit, author · relative time, full-history link

Research findings (gating question from PLAN)

  • mystmd has no built-in git/last-modified support — upstream request ENH: auto updating last modified date jupyter-book/mystmd#2213 is open with no traction.
  • Plugin transforms cannot inject frontmatter: page frontmatter is extracted before document-stage transforms run (myst-cli/src/process/mdast.ts), and myst-cli passes no options to transform plugins. The page AST does flow into the page JSON intact, so the plugin attaches mdast.data.git_metadata there (verified end-to-end against both the QuantEcon mystmd fork and the standard plugin API).
  • Per-page site: frontmatter passes through (validated as a plain object, deferred to the site template) — used as the manual override and as the deterministic data source for visual snapshots.

Implementation

  • plugins/git-metadata.mjsdocument-stage transform; git log --follow per source file (5s timeout); silent no-op for untracked files / non-git checkouts / missing git; QE_GIT_METADATA_MAX caps entries (default 10). Single self-contained .mjs, copyable into lecture repos — the shared quantecon-myst-plugins home question stays open (noted in PLAN.md).
  • app/components/PageHeaderHistory.tsx — Radix popover (Esc-close, ARIA, dark mode, QuantEcon blue); reads site.git_metadata override first, then the injected AST data; renders nothing when neither exists. Relative times are computed at render and gated behind mount so statically exported HTML hydrates cleanly. Commit links keep the .myst suffix (they target the source repo, unlike LaunchButton's notebook URLs); the full-history link derives from the mystmd-computed source_url.
  • Rendered from ProjectFrontmatter.tsx inside the bordered header block.

Tests

  • npm run test:plugin (new, wired into the visual CI job): node-level e2e that builds a throwaway MyST project with the real myst CLI and asserts the injected AST data — changelog ordering, pipe-in-subject handling, ISO dates, untracked-page no-op. Passes locally in ~1s.
  • Visual: fixture features.md pins deterministic git_metadata; new history-open test freezes the clock (page.clock.setFixedTime) and asserts button text, commit/full-history hrefs, relative times, Esc-close, plus a viewport snapshot. All 10 desktop+mobile tests and the FOUC guard pass locally.
  • -darwin baselines refreshed (features + new history-open). -linux baselines need a /update-snapshots comment on this PR — the visual job will fail until that runs.
  • Also fixes the stale quantecon-theme-src GitHub URL in the fixture's myst.yml.in.

Notes for review

  • Netlify preview generation: this PR is intended as the test case once the Netlify setup lands.
  • Lecture repos consume this by copying/referencing the plugin and adding it to project.plugins — see the new README section.

🤖 Generated with Claude Code

Reproduce the quantecon-book-theme page header: a "Last changed: <date>"
control that expands a changelog dropdown of recent commits, with
GitHub-linked hashes and a full-history link.

Build side: mystmd has no built-in last-modified support (upstream
request jupyter-book/mystmd#2213 is open), and plugin transforms cannot
modify page frontmatter (it is extracted before document-stage
transforms run), so plugins/git-metadata.mjs attaches
`git log --follow` results to the page AST as mdast.data.git_metadata,
which flows into the page JSON. Per-page `site.git_metadata`
frontmatter acts as a manual override.

Theme side: PageHeaderHistory.tsx (Radix popover; Esc/ARIA; dark mode;
relative times computed at render and gated behind mount so statically
exported HTML hydrates cleanly). Commit links keep the `.myst` suffix —
they target the source repo, unlike LaunchButton's notebook URLs; the
full-history link derives from the mystmd-computed source_url.

Tests: node-level plugin e2e (npm run test:plugin, wired into the
visual CI job) builds a throwaway project with the real myst CLI; the
visual fixture pins deterministic git_metadata on features.md and a new
history-open snapshot covers the open dropdown under a frozen clock.
-darwin baselines refreshed; -linux to follow via /update-snapshots.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 11, 2026 23:20
@mmcky

mmcky commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

/update-snapshots

@github-actions

Copy link
Copy Markdown
Contributor

🎭 Refreshed visual baselines in e007bf7:

  • tests/visual/snapshots/desktop-chrome-linux/features.png
  • tests/visual/snapshots/desktop-chrome-linux/history-open.png
  • tests/visual/snapshots/mobile-chrome-linux/features.png
  • tests/visual/snapshots/mobile-chrome-linux/history-open.png

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

🎭 Visual regression results

passed  11 passed
skipped  1 skipped

Details

stats  12 tests across 1 suite
duration  24.6 seconds
commit  eae42d2

Skipped tests

mobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › launch-colab

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds build-time Git metadata extraction and a theme UI control to show “Last changed: ” with an expandable per-page changelog in the page header, mirroring quantecon-book-theme.

Changes:

  • Add a MyST document-stage transform plugin (plugins/git-metadata.mjs) that injects per-page git history into mdast.data.git_metadata, plus a node-level e2e test.
  • Add a new header component (PageHeaderHistory) rendered in ProjectFrontmatter to display last-modified + a Radix popover changelog with commit/history links.
  • Extend CI + visual fixtures/snapshots and update docs (README/PLAN/CHANGELOG) to describe usage and deterministic testing.

Reviewed changes

Copilot reviewed 13 out of 21 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/visual/theme.spec.ts Adds a visual test covering opening/closing the history popover and verifying link targets/timestamps.
tests/visual/fixture/myst.yml.in Updates fixture repo URL to the correct GitHub repository for link generation.
tests/visual/fixture/features.md Pins deterministic site.git_metadata so visual snapshots don’t churn with real git timestamps.
tests/plugin/git-metadata.test.mjs Adds e2e test that runs the real myst CLI and asserts mdast.data.git_metadata injection behavior.
README.md Documents the new git-metadata plugin and header behavior, including configuration and overrides.
plugins/git-metadata.mjs Implements the MyST transform plugin that runs git log --follow and attaches results to the page AST.
PLAN.md Marks Phase 1 tasks as completed and records findings/implementation notes.
package.json Adds test:plugin script to run node’s test runner for plugin e2e.
CHANGELOG.md Records the new “Git history in page headers” feature in Unreleased.
app/types.ts Adds git_metadata to TemplateOptions for the page-level override channel.
app/components/ProjectFrontmatter.tsx Renders PageHeaderHistory in the bordered header block.
app/components/PageHeaderHistory.tsx New Radix popover UI for last-changed + changelog, reading override first then injected AST data.
.github/workflows/ci.yml Runs the new plugin e2e test in the visual job after building the theme template.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/visual/theme.spec.ts
Comment thread app/components/PageHeaderHistory.tsx Outdated
Comment thread app/components/PageHeaderHistory.tsx Outdated
… is empty

Address two Copilot review points on #83: only rewrite source_url into a
commits view when it actually has the /blob/ shape (otherwise render no
full-history link), and render the last-changed line as plain text when
there are no changelog entries instead of a non-functional popover
trigger. No pixel change — the fixture exercises the populated path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mmcky

mmcky commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Addressed Copilot's 3 comments: 2 fixed in d68317a (full-history link shape guard; plain text instead of a dead popover trigger when the changelog is empty — no pixel change, all 10 visual tests + plugin e2e green), 1 push-back with verification (clock.setFixedTime works standalone without clock.install()).

mmcky added a commit that referenced this pull request Jun 12, 2026
…ing) (#84)

* ci: per-PR rendered previews on GitHub Pages

Add preview.yml: on each same-repo PR, build the theme, statically
build the real QuantEcon/lecture-python-programming lectures with it
(myst build --html, BASE_URL set to the Pages subpath), and deploy to
the gh-pages branch under pr-preview/pr-<n>/ via rossjrw/pr-preview-action
(sticky link comment, teardown on close). Self-contained on
GITHUB_TOKEN - the org-secrets blocker that deferred the Netlify
approach is gone.

The content repo is a legacy Jupyter Book, so the build pipes a
confirmation into `myst init` to run the JB->MyST upgrade at build
time - the preview doubles as a migration-readiness check. Content is
cloned at full depth so the git-metadata plugin (PR #83) renders real
per-page history once it lands; a guard skips plugin injection on
branches that predate it.

Static export is the production path the Playwright harness (live
`myst start`) never exercises; the preview is qualitative and
non-gating - Playwright remains the only gate. Validated end-to-end
locally: JB upgrade under non-TTY stdin, yq config patching, 4.6s/45MB
build, BASE_URL-prefixed assets, SSR'd history header on real git data.

gh-pages was seeded with a root .nojekyll (Pages would otherwise drop
the underscore-prefixed build/_assets/ paths) and a landing index.html;
Pages serves from the gh-pages branch root.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci: bump preview.yml actions to Node 24 runtimes

Same checkout/setup-node v4 -> v5 bump as #85 (lowest node24 majors,
pure runtime ports). rossjrw/pr-preview-action@v1 stays: its internal
JamesIves/marocchino pins are upstream's to update and run under the
forced node24 runtime from 2026-06-16.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://QuantEcon.github.io/quantecon-theme.mystmd/pr-preview/pr-83/

Built to branch gh-pages at 2026-06-18 11:56 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@mmcky

mmcky commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@DrDrij @jstac I am currently migrating our features from quantecon-book-theme to the mystmd theme. This one is implementing the git history. I quite like the style we have on:

https://python-programming.quantecon.org/python_by_example.html

with a drop down integrated into the header bar.

Here is the first pass implementation -- it is an interesting option so I thought I would see what you thought before iterating to match.

https://quantecon.github.io/quantecon-theme.mystmd/pr-preview/pr-83/python-by-example/

It presents a window instead. 🤔

Note: don't worry about the broken html below the preview :-)

@jstac

jstac commented Jun 13, 2026

Copy link
Copy Markdown

Looks great to me @mmcky

I would shrink the size of the qe logo in the right hand menu somewhat, since it's quite bright and maybe distracting for readers, and perhaps lift it up to the top of that menu (again to make it less distracting).

@DrDrij

DrDrij commented Jun 13, 2026

Copy link
Copy Markdown
Member

UI/UX thoughts

I like it a lot, the fact the modal has scroll so uses up less space if there is a lot of history.

It does introduce additional accessibility checks. But if we can use a modal element, it should take care of desktop/mobile positioning. Perhaps centred in the browser window might be clearer and not in conflict with our lhs/rhs menus?

Safari gives it a nice blue border, Chrome needs something for it to stand out a little more.

image

While on the topic, What if the 'Last modified' was shifted inline with author names on the right? Saves vertical space and more semantic having it aside.

image

@jstac

jstac commented Jun 13, 2026

Copy link
Copy Markdown

+1 to shifting inline with names.

thanks @DrDrij

@mmcky

mmcky commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

I agree -- let's shift to inline with names. thanks @jstac and @DrDrij

Acts on the team's review of #83 (jstac, DrDrij):
- Move the "Last changed: <date>" control onto the author row, aligned
  right (more semantic, saves a header line); it wraps below authors on
  narrow viewports and renders nothing on pages without git metadata.
- Replace the anchored Radix popover with a centred Radix Dialog modal so
  the changelog clears the left/right page menus and centres on mobile.
  Adds a QuantEcon-blue border + dim backdrop so it stands out in Chrome
  (Safari already drew its own outline) and a close (X) button alongside
  Esc / backdrop-click.

Fixture sets authors on features.md so the snapshots exercise the new
authors-left / last-changed-right layout. -darwin baselines refreshed
(features + history-open, desktop + mobile); -linux baselines need a
/update-snapshots comment on the PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mmcky

mmcky commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

/update-snapshots

@mmcky

mmcky commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Pushed the first iteration acting on the review (2b5b3fe):

  • "Last changed" is now inline with the author names, aligned to the right of the same row (per @DrDrij / @jstac). It wraps below the authors at narrow/mobile widths, and pages without git history show nothing there.
  • The changelog now opens as a centred modal rather than an anchored dropdown (per @DrDrij), so it clears the left/right page menus and centres cleanly on mobile. It has a QuantEcon-blue border and a dim backdrop so it stands out in Chrome (Safari already drew its own outline), plus an ✕ button, Esc, and backdrop-click to close.

The QE-logo tweak (@jstac) is a right-hand-sidebar/theme item rather than part of this changelog control, so I've split it out into #96.

@jstac @DrDrij — the open question before I polish further: do you prefer this centred modal, or a slide-down panel that drops out of the header inline (closer to the current book-theme dropdown on python-programming that I linked)? Trade-offs as I see them: the modal is unambiguous on mobile and can't collide with the side menus, but it's a heavier "window" interaction; the slide-down keeps things lighter and anchored to the control, but needs care so it doesn't conflict with the lhs/rhs menus (the thing that pushed us to a modal in the first place). Happy to prototype the slide-down as well so we can compare side by side.

The rendered preview will refresh once the gh-pages deploy for this push completes: https://quantecon.github.io/quantecon-theme.mystmd/pr-preview/pr-83/

@github-actions

Copy link
Copy Markdown
Contributor

🎭 Refreshed visual baselines in 8c0229f:

  • tests/visual/snapshots/desktop-chrome-linux/features.png
  • tests/visual/snapshots/desktop-chrome-linux/history-open.png
  • tests/visual/snapshots/mobile-chrome-linux/features.png
  • tests/visual/snapshots/mobile-chrome-linux/history-open.png

… trigger CI)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jstac

jstac commented Jun 18, 2026

Copy link
Copy Markdown

@mmcky would you mind showing me a side-by-side comparison of the feature you want input on?

@mmcky

mmcky commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

@jstac this is our (a) CURRENT on quantecon-bok-theme vs (b) this modal implementation (cc @DrDrij)

qe-modal-vs-booktheme

(a) slides down on the page and collapses back up
(b) is a modal that you can dismiss

Note: (a) is from quantecon-book-theme so we would adapt it for this mystmd theme. So mainly thinking through mechanism.

@jstac

jstac commented Jun 18, 2026

Copy link
Copy Markdown

My vote is for (a) but I like both.

@mmcky

mmcky commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

@DrDrij any thoughts on this one?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants